<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>弹簧振子实验</title>
        <!-- Favicon -->
        <link rel="icon" type="image/svg+xml" href="/favicon.svg">
        <link rel="icon" type="image/png" href="/favicon.png">
        <link rel="apple-touch-icon" href="/apple-touch-icon.png">
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- KaTeX CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
    <style>
        :root {
            /* MD3 颜色系统 - Light主题 */
            --md-sys-color-primary: #6750A4;
            --md-sys-color-on-primary: #FFFFFF;
            --md-sys-color-primary-container: #EADDFF;
            --md-sys-color-on-primary-container: #21005E;
            --md-sys-color-secondary: #625B71;
            --md-sys-color-on-secondary: #FFFFFF;
            --md-sys-color-secondary-container: #E8DEF8;
            --md-sys-color-on-secondary-container: #1E192B;
            --md-sys-color-tertiary: #7D5260;
            --md-sys-color-on-tertiary: #FFFFFF;
            --md-sys-color-tertiary-container: #FFD8E4;
            --md-sys-color-on-tertiary-container: #370B1E;
            --md-sys-color-error: #B3261E;
            --md-sys-color-on-error: #FFFFFF;
            --md-sys-color-error-container: #F9DEDC;
            --md-sys-color-on-error-container: #370B1E;
            --md-sys-color-background: #FFFBFE;
            --md-sys-color-on-background: #1C1B1F;
            --md-sys-color-surface: #FFFBFE;
            --md-sys-color-on-surface: #1C1B1F;
            --md-sys-color-surface-variant: #E7E0EC;
            --md-sys-color-on-surface-variant: #49454F;
            --md-sys-color-outline: #79747E;
            --md-sys-color-success: #4CAF50;
        }

        body {
            font-family: 'Roboto', sans-serif;
            background-color: var(--md-sys-color-background);
            color: var(--md-sys-color-on-background);
            margin: 0;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }

        .md3-card {
            background-color: var(--md-sys-color-surface);
            border-radius: 12px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
            padding: 24px;
            margin-bottom: 24px;
            border: 1px solid var(--md-sys-color-outline);
        }

        .card-header {
            font-size: 1.375rem;
            font-weight: 500;
            margin-bottom: 16px;
            color: var(--md-sys-color-primary);
        }

        .card-body {
            font-size: 1rem;
            line-height: 1.5;
            color: var(--md-sys-color-on-surface-variant);
        }

        .formula {
            background-color: var(--md-sys-color-surface-variant);
            color: var(--md-sys-color-on-surface-variant);
            padding: 16px;
            border-radius: 8px;
            text-align: center;
            margin: 20px 0;
            font-family: 'Roboto Mono', monospace;
            font-size: 1.1rem;
        }

        .btn-primary {
            background-color: var(--md-sys-color-primary);
            border: none;
            padding: 10px 24px;
            border-radius: 20px;
            color: var(--md-sys-color-on-primary);
            font-weight: 500;
            text-transform: none;
            transition: background-color 0.3s ease;
            white-space: nowrap;
            min-width: 100px;
            text-align: center;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .btn-primary:hover {
            background-color: var(--md-sys-color-primary);
            opacity: 0.9;
        }

        .slider-container {
            margin: 16px 0;
            padding: 4px 0;
        }

        .slider-label {
            display: flex;
            justify-content: space-between;
            margin-bottom: 12px;
            font-size: 0.875rem;
            color: var(--md-sys-color-on-surface-variant);
        }

        input[type="range"] {
            width: 100%;
            height: 4px;
            background: var(--md-sys-color-primary-container);
            border-radius: 2px;
            -webkit-appearance: none;
            appearance: none;
            cursor: pointer;
        }

        input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 20px;
            height: 20px;
            background: var(--md-sys-color-primary);
            border-radius: 50%;
            cursor: pointer;
        }

        input[type="range"]::-moz-range-thumb {
            width: 20px;
            height: 20px;
            background: var(--md-sys-color-primary);
            border-radius: 50%;
            cursor: pointer;
            border: none;
        }

        /* MD3 Checkbox Styles */
        .form-check {
            display: flex;
            align-items: center;
            margin-bottom: 8px;
            padding: 4px 0;
            min-height: 32px;
            position: relative;
        }

        .md3-checkbox {
            position: relative;
            width: 18px;
            height: 18px;
            margin-right: 12px;
            display: inline-block;
            cursor: pointer;
        }

        .md3-checkbox input[type="checkbox"] {
            opacity: 0;
            width: 18px;
            height: 18px;
            margin: 0;
            position: absolute;
            left: 0;
            top: 0;
            cursor: pointer;
            z-index: 2;
        }

        .md3-checkbox .md3-checkmark {
            position: absolute;
            left: 0;
            top: 0;
            width: 18px;
            height: 18px;
            background: var(--md-sys-color-surface);
            border: 2px solid var(--md-sys-color-outline);
            border-radius: 50%;
            transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
            box-sizing: border-box;
            z-index: 1;
        }

        .md3-checkbox input[type="checkbox"]:checked + .md3-checkmark {
            background: var(--md-sys-color-primary);
            border-color: var(--md-sys-color-primary);
        }

        .md3-checkbox input[type="checkbox"]:hover + .md3-checkmark {
            border-color: var(--md-sys-color-primary);
        }

        .md3-checkbox input[type="checkbox"]:focus + .md3-checkmark {
            border-color: var(--md-sys-color-primary);
            box-shadow: 0 0 0 2px rgba(103, 80, 164, 0.2);
        }

        .md3-checkbox .md3-checkmark svg {
            display: none;
            position: absolute;
            left: 3px;
            top: 3px;
            width: 12px;
            height: 12px;
            stroke: var(--md-sys-color-on-primary);
            stroke-width: 2;
            stroke-linecap: round;
            stroke-linejoin: round;
            fill: none;
        }

        .md3-checkbox input[type="checkbox"]:checked + .md3-checkmark svg {
            display: block;
            animation: checkmark 0.2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
        }

        @keyframes checkmark {
            0% {
                stroke-dashoffset: 22;
            }
            100% {
                stroke-dashoffset: 0;
            }
        }

        .form-check-label {
            font-size: 0.875rem;
            color: var(--md-sys-color-on-surface);
            cursor: pointer;
            user-select: none;
            transition: color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
        }

        .md3-checkbox input[type="checkbox"]:hover ~ .form-check-label {
            color: var(--md-sys-color-primary);
        }

        #canvas-container {
            width: 100%;
            height: 400px;
            background-color: var(--md-sys-color-surface-variant);
            border-radius: 12px;
            overflow: hidden;
            position: relative;
            border: 1px solid var(--md-sys-color-outline);
        }

        .data-display {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
            margin-top: 24px;
        }

        .data-item {
            background-color: var(--md-sys-color-surface-variant);
            padding: 16px;
            border-radius: 8px;
            text-align: center;
        }

        .data-label {
            font-size: 1rem;
            font-weight: 500;
            color: var(--md-sys-color-secondary);
            margin-bottom: 8px;
            display: block;
        }

        .data-value {
            font-size: 1.75rem;
            font-weight: 700;
            color: var(--md-sys-color-primary);
            line-height: 1.2;
        }

        .text-success {
            color: var(--md-sys-color-success);
        }

        .text-danger {
            color: var(--md-sys-color-error);
        }

        .theory-section {
            background-color: var(--md-sys-color-surface);
            border-radius: 12px;
            padding: 24px;
            margin-bottom: 24px;
            border: 1px solid var(--md-sys-color-outline);
        }

        .theory-section h3 {
            color: var(--md-sys-color-primary);
            font-size: 1.375rem;
            font-weight: 500;
            margin-bottom: 16px;
        }

        .theory-section p {
            font-size: 1rem;
            line-height: 1.6;
            margin-bottom: 16px;
            color: var(--md-sys-color-on-surface-variant);
        }

        .highlight {
            background-color: var(--md-sys-color-primary-container);
            color: var(--md-sys-color-on-primary-container);
            padding: 2px 8px;
            border-radius: 4px;
            font-weight: 500;
        }

        .formula.highlight {
            background-color: var(--md-sys-color-primary-container);
            color: var(--md-sys-color-on-primary-container);
            border: 1px solid var(--md-sys-color-primary);
        }

        /* KaTeX 公式样式 */
        .katex-formula {
            background-color: var(--md-sys-color-surface-variant);
            color: var(--md-sys-color-on-surface-variant);
            padding: 16px;
            border-radius: var(--md-sys-shape-corner-small);
            text-align: center;
            margin: 20px 0;
            font-size: 1.1rem;
            overflow-x: auto;
        }

        .katex-formula.highlight {
            background-color: var(--md-sys-color-primary-container);
            color: var(--md-sys-color-on-primary-container);
            border: 1px solid var(--md-sys-color-primary);
        }

        /* 确保KaTeX公式在容器中居中 */
        .katex {
            display: inline-block;
            text-align: center;
        }

        /* 调整KaTeX公式的字体大小 */
        .katex {
            font-size: 1.1em;
        }

        .theory-tips {
            background-color: var(--md-sys-color-surface-variant);
            padding: 20px;
            border-radius: var(--md-sys-shape-corner-medium);
            margin: 20px 0;
        }

        .theory-tips h3 {
            color: var(--md-sys-color-on-surface-variant);
            margin-bottom: 16px;
        }

        .theory-tips ul {
            list-style: none;
            padding-left: 20px;
        }

        .theory-tips li {
            margin: 8px 0;
            color: var(--md-sys-color-on-surface-variant);
        }

        /* 添加模态框样式 */
        .modal {
            display: none;
            position: fixed;
            z-index: 1000;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.4);
        }

        .modal-content {
            background-color: var(--md-sys-color-surface);
            margin: 15% auto;
            padding: 20px;
            border: 1px solid var(--md-sys-color-outline);
            width: 80%;
            max-width: 500px;
            border-radius: 12px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }

        .modal-header {
            color: var(--md-sys-color-primary);
            font-size: 1.25rem;
            font-weight: 500;
            margin-bottom: 16px;
            text-align: center;
        }

        .modal-body {
            color: var(--md-sys-color-on-surface-variant);
            margin-bottom: 20px;
            text-align: center;
        }

        .modal-footer {
            text-align: center;
        }

        .modal-button {
            background-color: var(--md-sys-color-primary);
            color: var(--md-sys-color-on-primary);
            border: none;
            padding: 10px 24px;
            border-radius: 20px;
            font-weight: 500;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }

        .modal-button:hover {
            background-color: var(--md-sys-color-primary);
            opacity: 0.9;
        }

        .close {
            color: var(--md-sys-color-outline);
            float: right;
            font-size: 28px;
            font-weight: bold;
            cursor: pointer;
        }

        .close:hover {
            color: var(--md-sys-color-error);
        }
    </style>
</head>
<body>
    <!-- 添加模态框 -->
    <div id="energyModal" class="modal">
        <div class="modal-content">
            <div class="modal-header">能量过低</div>
            <div class="modal-body">
                当前系统的总能量已低于0.001J，后续运动可以忽略不计。<br>
                请点击下方按钮重置实验。
            </div>
            <div class="modal-footer">
                <button class="modal-button" onclick="resetSimulation(); closeModal(); document.getElementById('damping-container').style.display = 'none';">重置实验</button>
            </div>
        </div>
    </div>

    <div class="container">
        <div class="row">
            <!-- 左侧控制面板 -->
            <div class="col-md-4">
                <div class="md3-card">
                    <h2 class="card-header">弹簧振子实验</h2>
                    <div class="card-body">
                        <!-- 弹簧常数控制 -->
                        <div class="slider-container">
                            <div class="slider-label">
                                <label for="spring-constant">弹簧常数 k (N/m)</label>
                                <span id="spring-constant-value">10</span>
                            </div>
                            <input type="range" id="spring-constant" min="1" max="50" step="1" value="10">
                        </div>

                        <!-- 质量控制 -->
                        <div class="slider-container">
                            <div class="slider-label">
                                <label for="mass">质量 m (kg)</label>
                                <span id="mass-value">1.0</span>
                            </div>
                            <input type="range" id="mass" min="0.1" max="5" step="0.1" value="1.0">
                        </div>

                        <!-- 振幅控制 -->
                        <div class="slider-container">
                            <div class="slider-label">
                                <label for="amplitude">振幅 A (m)</label>
                                <span id="amplitude-value">0.2</span>
                            </div>
                            <input type="range" id="amplitude" min="0.05" max="0.4" step="0.01" value="0.2">
                        </div>

                        <!-- 阻尼系数控制 -->
                        <div class="slider-container" id="damping-container" style="display: none;">
                            <div class="slider-label">
                                <label for="damping">阻尼系数 b</label>
                                <span id="damping-value">0</span>
                            </div>
                            <input type="range" id="damping" min="0" max="1" step="0.1" value="0">
                        </div>

                        <!-- 显示控制 -->
                        <div class="slider-container">
                            <div class="form-check">
                                <span class="md3-checkbox">
                                    <input type="checkbox" id="show-velocity" checked>
                                    <span class="md3-checkmark">
                                        <svg viewBox="0 0 18 18">
                                            <path d="M3,9 L7,13 L15,5" stroke-dasharray="22" stroke-dashoffset="22"/>
                                        </svg>
                                    </span>
                                </span>
                                <label class="form-check-label" for="show-velocity">显示速度矢量</label>
                            </div>
                            <div class="form-check">
                                <span class="md3-checkbox">
                                    <input type="checkbox" id="show-acceleration">
                                    <span class="md3-checkmark">
                                        <svg viewBox="0 0 18 18">
                                            <path d="M3,9 L7,13 L15,5" stroke-dasharray="22" stroke-dashoffset="22"/>
                                        </svg>
                                    </span>
                                </span>
                                <label class="form-check-label" for="show-acceleration">显示加速度矢量</label>
                            </div>
                            <div class="form-check">
                                <span class="md3-checkbox">
                                    <input type="checkbox" id="show-trace" checked>
                                    <span class="md3-checkmark">
                                        <svg viewBox="0 0 18 18">
                                            <path d="M3,9 L7,13 L15,5" stroke-dasharray="22" stroke-dashoffset="22"/>
                                        </svg>
                                    </span>
                                </span>
                                <label class="form-check-label" for="show-trace">显示运动轨迹</label>
                            </div>
                            <div class="form-check">
                                <span class="md3-checkbox">
                                    <input type="checkbox" id="show-energy" checked>
                                    <span class="md3-checkmark">
                                        <svg viewBox="0 0 18 18">
                                            <path d="M3,9 L7,13 L15,5" stroke-dasharray="22" stroke-dashoffset="22"/>
                                        </svg>
                                    </span>
                                </span>
                                <label class="form-check-label" for="show-energy">显示能量图表</label>
                            </div>
                        </div>

                        <!-- 控制按钮 -->
                        <div class="d-flex gap-2 mt-3">
                            <button id="toggle-btn" class="btn btn-primary">开始模拟</button>
                            <button id="reset-btn" class="btn btn-primary">重置</button>
                            <a href="/" class="btn btn-primary">返回主页</a>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 右侧模拟区域 -->
            <div class="col-md-8">
                <div class="md3-card">
                    <div id="canvas-container"></div>

                    <!-- 数据显示 -->
                    <div class="data-display">
                        <div class="data-item">
                            <div class="data-label">位移 x</div>
                            <div id="displacement" class="data-value">0.00 m</div>
                        </div>
                        <div class="data-item">
                            <div class="data-label">速度 v</div>
                            <div id="velocity" class="data-value">0.00 m/s</div>
                        </div>
                        <div class="data-item">
                            <div class="data-label">加速度 a</div>
                            <div id="acceleration" class="data-value">0.00 m/s²</div>
                        </div>
                        <div class="data-item">
                            <div class="data-label">总能量 E</div>
                            <div id="total-energy" class="data-value">0.00 J</div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 理论说明 -->
        <div class="row mt-4">
            <div class="col-12">
                <div class="theory-section">
                    <h3>简谐运动原理</h3>
                    <p>简谐运动是一种<span class="highlight">周期性运动</span>，其特点是物体受到的恢复力与位移成正比，方向相反。</p>
                    <div class="katex-formula highlight">
                        \[ F = -kx \]
                    </div>
                    <p>其中k是弹簧常数，x是位移。</p>
                </div>

                <div class="theory-section">
                    <h3>运动方程</h3>
                    <p>简谐运动的位移随时间变化满足：</p>
                    <div class="katex-formula">
                        \[ x(t) = A \cdot \cos(\omega t + \phi) \]
                    </div>
                    <p>其中A是振幅，ω是角频率，φ是初相位。</p>
                    <p>角频率与弹簧常数和质量的关系：</p>
                    <div class="katex-formula">
                        \[ \omega = \sqrt{\frac{k}{m}} \]
                    </div>
                </div>

                <div class="theory-section">
                    <h3>能量分析</h3>
                    <p>在简谐运动中，能量在动能和势能之间不断转换，但总能量保持不变：</p>
                    <div class="katex-formula">
                        \[ E = \frac{1}{2}kA^2 = \frac{1}{2}mv_{\text{max}}^2 \]
                    </div>
                    <p>其中vₘₐₓ是最大速度。</p>
                </div>
            </div>
        </div>
    </div>

    <!-- KaTeX JS -->
    <script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"></script>
    <script>
        // 全局变量
        let p5Instance;
        let simulationRunning = false;
        let time = 0;
        let lastTime = 0;
        let xPos = 0;
        let velocity = 0;
        let acceleration = 0;
        let lastXPos = 0;
        let lastVelocity = 0;
        let lastAcceleration = 0;
        let omega = 0;
        let amplitude = 0.2;
        let damping = 0;
        let showVelocity = true;
        let showAcceleration = false;
        let showTrace = true;
        let showEnergy = true;
        let tracePoints = [];
        const maxTracePoints = 200;
        let energyData = [];
        const maxEnergyPoints = 200;
        const ENERGY_THRESHOLD = 0.001;
        let energyBelowThreshold = false;

        // 显示模态框
        function showModal() {
            const modal = document.getElementById('energyModal');
            modal.style.display = 'block';
            energyBelowThreshold = true;
        }

        // 关闭模态框
        function closeModal() {
            const modal = document.getElementById('energyModal');
            modal.style.display = 'none';
            energyBelowThreshold = false;
        }

        // 初始化 p5.js 画布
        function initSimulation() {
            const container = document.getElementById('canvas-container');
            p5Instance = new p5(sketch, container);
        }

        // p5.js 草图
        const sketch = (p) => {
            p.setup = () => {
                const container = document.getElementById('canvas-container');
                const canvas = p.createCanvas(container.clientWidth, container.clientHeight);
                canvas.parent('canvas-container');
                
                // 初始化参数
                const k = parseFloat(document.getElementById('spring-constant').value);
                const m = parseFloat(document.getElementById('mass').value);
                omega = Math.sqrt(k / m);
                amplitude = parseFloat(document.getElementById('amplitude').value);
                damping = parseFloat(document.getElementById('damping').value);
                
                // 初始化位置
                xPos = amplitude;
                velocity = 0;
                time = 0;
                lastTime = 0;
                
                // 清空轨迹和能量数据
                tracePoints = [];
                energyData = [];
                energyBelowThreshold = false;
            };

            p.draw = () => {
                // 清空画布
                p.background(255);
                
                // 获取当前参数
                const k = parseFloat(document.getElementById('spring-constant').value);
                const m = parseFloat(document.getElementById('mass').value);
                amplitude = parseFloat(document.getElementById('amplitude').value);
                damping = parseFloat(document.getElementById('damping').value);
                
                // 计算角频率
                omega = Math.sqrt(k / m);
                
                // 更新时间和物理量（如果未暂停且能量未低于阈值）
                if (simulationRunning && !energyBelowThreshold) {
                    const dt = 0.016; // 假设60fps，约16ms
                    time += dt;
                    
                    // 计算新的位置和速度
                    if (damping === 0) {
                        // 无阻尼简谐运动解析解
                        xPos = amplitude * Math.cos(omega * time);
                        velocity = -amplitude * omega * Math.sin(omega * time);
                        acceleration = -k * xPos / m;
                    } else {
                        // 有阻尼的数值积分（简化的欧拉方法）
                        acceleration = (-k * xPos - damping * velocity) / m;
                        velocity += acceleration * dt;
                        xPos += velocity * dt;
                    }
                    
                    // 计算能量
                    const kineticEnergy = 0.5 * m * velocity * velocity;
                    const potentialEnergy = 0.5 * k * xPos * xPos;
                    const totalEnergy = kineticEnergy + potentialEnergy;
                    
                    // 检查能量是否低于阈值
                    if (damping > 0 && totalEnergy < ENERGY_THRESHOLD) {
                        simulationRunning = false;
                        // 保持最后的位置和速度
                        lastXPos = xPos;
                        lastVelocity = velocity;
                        lastAcceleration = acceleration;
                        showModal();
                    }
                    
                    // 添加到轨迹
                    if (showTrace) {
                        const traceX = p.width * 0.35 + xPos * 200;
                        const traceY = p.height * 0.5;
                        
                        tracePoints.push({
                            x: traceX,
                            y: traceY
                        });
                        
                        if (tracePoints.length > maxTracePoints) {
                            tracePoints.shift();
                        }
                    }
                    
                    // 添加到能量数据
                    if (showEnergy) {
                        energyData.push({
                            total: totalEnergy,
                            kinetic: kineticEnergy,
                            potential: potentialEnergy
                        });
                        
                        if (energyData.length > maxEnergyPoints) {
                            energyData.shift();
                        }
                    }
                } else {
                    // 使用最后的位置和速度
                    xPos = lastXPos;
                    velocity = lastVelocity;
                    acceleration = lastAcceleration;
                }
                
                // 更新显示的物理量
                updatePhysicalValues();
                
                // 绘制场景（无论是否运行都绘制）
                drawScene();
            };

            p.windowResized = () => {
                const container = document.getElementById('canvas-container');
                p.resizeCanvas(container.clientWidth, container.clientHeight);
            };
        };

        // 绘制场景
        function drawScene() {
            const p = p5Instance;
            
            // 绘制坐标系
            p.stroke(200);
            p.strokeWeight(1);
            p.line(0, p.height * 0.5, p.width, p.height * 0.5);
            
            // 绘制弹簧和质量块
            const centerX = p.width * 0.35;
            const centerY = p.height * 0.5;
            const springLength = 100;
            const massSize = 30;
            const scaleFactor = 200;
            
            // 绘制弹簧
            p.stroke(94, 106, 210);
            p.strokeWeight(2);
            p.noFill();
            
            const startX = centerX - springLength;
            const endX = centerX + xPos * scaleFactor;
            
            // 绘制弹簧螺旋
            const numCoils = 14;
            const coilHeight = 14;
            const coilWidth = (endX - startX) / numCoils;
            p.beginShape();
            for (let i = 0; i <= numCoils; i++) {
                const x = startX + i * coilWidth;
                let y = centerY;
                if (i === 0 || i === numCoils) {
                    y = centerY;
                } else {
                    y = centerY + (i % 2 === 0 ? -coilHeight : coilHeight);
                }
                p.vertex(x, y);
            }
            p.endShape();
            
            // 绘制质量块
            p.fill(94, 106, 210);
            p.noStroke();
            p.drawingContext.shadowColor = 'rgba(94,106,210,0.25)';
            p.drawingContext.shadowBlur = 16;
            p.ellipse(endX + massSize * 0.2, centerY, massSize * 1.2, massSize * 1.2);
            p.drawingContext.shadowBlur = 0;
            
            // 绘制质量块上的m
            p.fill(255);
            p.textAlign(p.CENTER, p.CENTER);
            p.textSize(massSize * 0.7);
            p.text('m', endX + massSize * 0.2, centerY + 2);
            
            // 绘制速度矢量
            if (showVelocity) {
                const velocityScale = 20;
                const velocityX = endX + massSize * 0.2;
                const velocityY = centerY;
                const velocityEndX = velocityX + velocity * velocityScale;
                const velocityEndY = velocityY;
                
                p.stroke(16, 185, 129);
                p.strokeWeight(2);
                p.line(velocityX, velocityY, velocityEndX, velocityEndY);
                
                // 绘制箭头
                const arrowSize = 8;
                const angle = Math.atan2(velocityEndY - velocityY, velocityEndX - velocityX);
                p.push();
                p.translate(velocityEndX, velocityEndY);
                p.rotate(angle);
                p.line(0, 0, -arrowSize, -arrowSize);
                p.line(0, 0, -arrowSize, arrowSize);
                p.pop();
                
                // 添加标签
                p.noStroke();
                p.fill(16, 185, 129);
                p.textSize(12);
                p.textAlign(p.CENTER, p.BOTTOM);
                p.text('v', velocityEndX, velocityEndY - 5);
            }
            
            // 绘制加速度矢量
            if (showAcceleration) {
                const accelerationScale = 100;
                const accelerationX = endX + massSize * 0.2;
                const accelerationY = centerY;
                const accelerationEndX = accelerationX + acceleration * accelerationScale;
                const accelerationEndY = accelerationY;
                
                p.stroke(245, 158, 11);
                p.strokeWeight(2);
                p.line(accelerationX, accelerationY, accelerationEndX, accelerationEndY);
                
                // 绘制箭头
                const arrowSize = 8;
                const angle = Math.atan2(accelerationEndY - accelerationY, accelerationEndX - accelerationX);
                p.push();
                p.translate(accelerationEndX, accelerationEndY);
                p.rotate(angle);
                p.line(0, 0, -arrowSize, -arrowSize);
                p.line(0, 0, -arrowSize, arrowSize);
                p.pop();
                
                // 添加标签
                p.noStroke();
                p.fill(245, 158, 11);
                p.textSize(12);
                p.textAlign(p.CENTER, p.BOTTOM);
                p.text('a', accelerationEndX, accelerationEndY - 5);
            }
            
            // 平衡位置虚线
            p.stroke(180);
            p.strokeWeight(1);
            p.drawingContext.setLineDash([6, 6]);
            p.line(centerX, centerY - 60, centerX, centerY + 60);
            p.drawingContext.setLineDash([]);
            
            // 平衡位置标记
            p.noStroke();
            p.fill(120);
            p.ellipse(centerX, centerY + 40, 18, 18);
            p.fill(255);
            p.ellipse(centerX, centerY + 40, 10, 10);
            p.fill(120);
            p.textSize(16);
            p.textAlign(p.CENTER, p.TOP);
            p.text('平衡位置', centerX, centerY + 52);
            
            // 位置刻度
            p.stroke(180);
            p.strokeWeight(1);
            for (let dx = -0.4; dx <= 0.4; dx += 0.1) {
                const x = centerX + dx * scaleFactor;
                p.line(x, centerY + 30, x, centerY + 38);
                if (dx % 0.2 === 0) {
                    p.noStroke();
                    p.fill(120);
                    p.textSize(13);
                    p.textAlign(p.CENTER, p.TOP);
                    p.text((dx >= 0 ? '+' : '') + dx.toFixed(1) + 'm', x, centerY + 44);
                    p.stroke(180);
                }
            }
            
            // 绘制轨迹
            if (showTrace && tracePoints.length > 1) {
                p.noFill();
                p.stroke(94, 106, 210, 150);
                p.strokeWeight(2);
                p.beginShape();
                for (let point of tracePoints) {
                    p.vertex(point.x, point.y);
                }
                p.endShape();
            }
            
            // 绘制能量图表
            if (showEnergy && energyData.length > 0) {
                const graphX = p.width * 0.6;
                const graphY = p.height * 0.3;
                const graphWidth = p.width * 0.35;
                const graphHeight = 200;
                
                // 绘制图表背景
                p.fill(255);
                p.stroke(200);
                p.strokeWeight(1);
                p.rect(graphX, graphY, graphWidth, graphHeight);
                
                // 找出能量最大值用于归一化
                let maxEnergy = 0;
                for (let data of energyData) {
                    maxEnergy = Math.max(maxEnergy, data.total);
                }
                maxEnergy = Math.max(maxEnergy, 0.001);
                
                // 绘制能量曲线
                const pointSpacing = graphWidth / maxEnergyPoints;
                
                // 总能量曲线
                p.stroke(147, 51, 234);
                p.strokeWeight(2);
                p.noFill();
                p.beginShape();
                for (let i = 0; i < energyData.length; i++) {
                    const x = graphX + i * pointSpacing;
                    const y = graphY + graphHeight - (energyData[i].total / maxEnergy) * graphHeight;
                    p.vertex(x, y);
                }
                p.endShape();
                
                // 动能曲线
                p.stroke(16, 185, 129);
                p.strokeWeight(1.5);
                p.beginShape();
                for (let i = 0; i < energyData.length; i++) {
                    const x = graphX + i * pointSpacing;
                    const y = graphY + graphHeight - (energyData[i].kinetic / maxEnergy) * graphHeight;
                    p.vertex(x, y);
                }
                p.endShape();
                
                // 势能曲线
                p.stroke(245, 158, 11);
                p.strokeWeight(1.5);
                p.beginShape();
                for (let i = 0; i < energyData.length; i++) {
                    const x = graphX + i * pointSpacing;
                    const y = graphY + graphHeight - (energyData[i].potential / maxEnergy) * graphHeight;
                    p.vertex(x, y);
                }
                p.endShape();
                
                // 添加图例
                p.noStroke();
                p.fill(147, 51, 234);
                p.textSize(12);
                p.textAlign(p.LEFT, p.CENTER);
                p.text('总能量', graphX + graphWidth + 10, graphY + 20);
                
                p.fill(16, 185, 129);
                p.text('动能', graphX + graphWidth + 10, graphY + 40);
                
                p.fill(245, 158, 11);
                p.text('势能', graphX + graphWidth + 10, graphY + 60);
            }
        }

        // 更新显示的物理量
        function updatePhysicalValues() {
            document.getElementById('displacement').textContent = xPos.toFixed(2) + " m";
            document.getElementById('velocity').textContent = velocity.toFixed(2) + " m/s";
            document.getElementById('acceleration').textContent = acceleration.toFixed(2) + " m/s²";
            
            const k = parseFloat(document.getElementById('spring-constant').value);
            const m = parseFloat(document.getElementById('mass').value);
            const kineticEnergy = 0.5 * m * velocity * velocity;
            const potentialEnergy = 0.5 * k * xPos * xPos;
            const totalEnergy = kineticEnergy + potentialEnergy;
            
            document.getElementById('total-energy').textContent = totalEnergy.toFixed(2) + " J";
        }

        // 重置模拟
        function resetSimulation() {
            // 停止模拟
            simulationRunning = false;
            energyBelowThreshold = false;
            
            // 重置时间
            time = 0;
            lastTime = 0;
            
            // 重置位置和速度到平衡位置
            xPos = 0;
            velocity = 0;
            acceleration = 0;
            lastXPos = 0;
            lastVelocity = 0;
            lastAcceleration = 0;
            
            // 重置阻尼系数
            const dampingSlider = document.getElementById('damping');
            dampingSlider.value = 0;
            document.getElementById('damping-value').textContent = '0';
            damping = 0;
            
            // 清空轨迹和能量数据
            tracePoints = [];
            energyData = [];
            
            // 更新显示的物理量（全部为0）
            document.getElementById('displacement').textContent = '0.00 m';
            document.getElementById('velocity').textContent = '0.00 m/s';
            document.getElementById('acceleration').textContent = '0.00 m/s²';
            document.getElementById('total-energy').textContent = '0.00 J';
            
            // 更新按钮文本
            document.getElementById('toggle-btn').textContent = '开始模拟';
            
            // 重新绘制场景
            if (p5Instance) {
                p5Instance.redraw();
            }
        }

        // 事件监听器
        document.addEventListener('DOMContentLoaded', () => {
            // 初始化模拟
            initSimulation();
            
            // 开始/停止按钮
            const toggleBtn = document.getElementById('toggle-btn');
            toggleBtn.addEventListener('click', () => {
                simulationRunning = !simulationRunning;
                toggleBtn.textContent = simulationRunning ? '停止模拟' : '开始模拟';
                
                // 控制阻尼系数滑块的显示/隐藏
                const dampingContainer = document.getElementById('damping-container');
                dampingContainer.style.display = simulationRunning ? 'block' : 'none';
                
                // 如果停止模拟，保持当前状态
                if (!simulationRunning) {
                    lastXPos = xPos;
                    lastVelocity = velocity;
                    lastAcceleration = acceleration;
                }
            });
            
            // 重置按钮
            document.getElementById('reset-btn').addEventListener('click', () => {
                resetSimulation();
                closeModal();
                
                // 隐藏阻尼系数滑块
                const dampingContainer = document.getElementById('damping-container');
                dampingContainer.style.display = 'none';
            });
            
            // 滑块值更新
            document.querySelectorAll('input[type="range"]').forEach(slider => {
                slider.addEventListener('input', (e) => {
                    const valueDisplay = document.getElementById(`${e.target.id}-value`);
                    valueDisplay.textContent = e.target.value;
                    
                    // 如果模拟未运行，更新显示的值
                    if (!simulationRunning) {
                        document.getElementById('displacement').textContent = '0.00 m';
                        document.getElementById('velocity').textContent = '0.00 m/s';
                        document.getElementById('acceleration').textContent = '0.00 m/s²';
                        document.getElementById('total-energy').textContent = '0.00 J';
                        
                        // 更新位置到平衡位置
                        xPos = 0;
                        lastXPos = 0;
                        
                        // 重新绘制场景
                        if (p5Instance) {
                            p5Instance.redraw();
                        }
                    }
                });
            });
            
            // 复选框更新
            document.getElementById('show-velocity').addEventListener('change', (e) => {
                showVelocity = e.target.checked;
                if (p5Instance) {
                    p5Instance.redraw();
                }
            });
            
            document.getElementById('show-acceleration').addEventListener('change', (e) => {
                showAcceleration = e.target.checked;
                if (p5Instance) {
                    p5Instance.redraw();
                }
            });
            
            document.getElementById('show-trace').addEventListener('change', (e) => {
                showTrace = e.target.checked;
                if (p5Instance) {
                    p5Instance.redraw();
                }
            });
            
            document.getElementById('show-energy').addEventListener('change', (e) => {
                showEnergy = e.target.checked;
                if (p5Instance) {
                    p5Instance.redraw();
                }
            });
        });

        // 渲染所有KaTeX公式
        document.addEventListener("DOMContentLoaded", function() {
            // 配置 KaTeX 自动渲染
            renderMathInElement(document.body, {
                delimiters: [
                    {left: "\\[", right: "\\]", display: true},
                    {left: "\\(", right: "\\)", display: false}
                ],
                throwOnError: false,
                strict: false,
                trust: true,
                macros: {
                    "\\phi": "\\varphi"  // 使用 varphi 替代 phi
                }
            });
        });
    </script>
</body>
</html>